home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / StyleContext.java < prev    next >
Text File  |  1998-06-30  |  49KB  |  1,457 lines

  1. /*
  2.  * @(#)StyleContext.java    1.46 98/04/09
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text;
  21.  
  22. import java.awt.Font;
  23. import java.awt.FontMetrics;
  24. import java.awt.Toolkit;
  25. import java.util.*;
  26. import java.io.*;
  27.  
  28. import com.sun.java.swing.SwingUtilities;
  29. import com.sun.java.swing.event.ChangeListener;
  30. import com.sun.java.swing.event.EventListenerList;
  31. import com.sun.java.swing.event.ChangeEvent;
  32.  
  33. /**
  34.  * A pool of styles and their associated resources.  This class determines
  35.  * the lifetime of a group of resources by being a container that holds
  36.  * caches for various resources such as font and color that get reused 
  37.  * by the various style definitions.  This can be shared by multiple
  38.  * documents if desired to maximize the sharing of related resources.
  39.  * <p>
  40.  * This class also provides efficient support for small sets of attributes
  41.  * and compresses them by sharing across uses and taking advantage of 
  42.  * their immutable nature.  Since many styles are replicated, the potential
  43.  * for sharing is significant, and copies can be extremely cheap.  
  44.  * Larger sets reduce the possibility of sharing, and therefore revert 
  45.  * automatically to a less space-efficient implementation.
  46.  * <p>
  47.  * Warning: serialized objects of this class will not be compatible with
  48.  * future swing releases.  The current serialization support is appropriate 
  49.  * for short term storage or RMI between Swing1.0 applications.  It will
  50.  * not be possible to load serialized Swing1.0 objects with future releases
  51.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  52.  * baseline for the serialized form of Swing objects.
  53.  *
  54.  * @author  Timothy Prinzing
  55.  * @version 1.46 04/09/98
  56.  */
  57. public class StyleContext implements Serializable, AbstractDocument.AttributeContext {
  58.  
  59.     /**
  60.      * Returns default AttributeContext shared by all documents that
  61.      * don't bother to define/supply their own context.
  62.      *
  63.      * @return the context
  64.      */
  65.     public static final StyleContext getDefaultStyleContext() {
  66.         if (defaultContext == null) {
  67.             defaultContext = new StyleContext();
  68.         }
  69.         return defaultContext;
  70.     }
  71.  
  72.     private static StyleContext defaultContext;
  73.  
  74.     /**
  75.      * Creates a new StyleContext object.
  76.      */
  77.     public StyleContext() {
  78.         styles = new NamedStyle(null);
  79.         addStyle(DEFAULT_STYLE, null);
  80.     }
  81.  
  82.     /**
  83.      * Adds a new style into the style hierarchy.  Style attributes
  84.      * resolve from bottom up so an attribute specified in a child
  85.      * will override an attribute specified in the parent.
  86.      *
  87.      * @param nm   the name of the style (must be unique within the
  88.      *   collection of named styles in the document).  The name may 
  89.      *   be null if the style is unnamed, but the caller is responsible
  90.      *   for managing the reference returned as an unnamed style can't
  91.      *   be fetched by name.  An unnamed style may be useful for things
  92.      *   like character attribute overrides such as found in a style 
  93.      *   run.
  94.      * @param parent the parent style.  This may be null if unspecified
  95.      *   attributes need not be resolved in some other style.
  96.      * @return the created style
  97.      */
  98.     public Style addStyle(String nm, Style parent) {
  99.         Style style = new NamedStyle(nm, parent); 
  100.         if (nm != null) {
  101.             // add a named style, a class of attributes
  102.             styles.addAttribute(nm, style);
  103.         }
  104.         return style;
  105.     }
  106.  
  107.     /**
  108.      * Removes a named style previously added to the document.  
  109.      *
  110.      * @param nm  the name of the style to remove
  111.      */
  112.     public void removeStyle(String nm) {
  113.         styles.removeAttribute(nm);
  114.     }
  115.  
  116.     /**
  117.      * Fetches a named style previously added to the document
  118.      *
  119.      * @param nm  the name of the style
  120.      * @return the style
  121.      */
  122.     public Style getStyle(String nm) {
  123.         return (Style) styles.getAttribute(nm);
  124.     }
  125.  
  126.     /**
  127.      * Fetches the names of the styles defined.
  128.      *
  129.      * @return the list of names as an enumeration
  130.      */
  131.     public Enumeration getStyleNames() {
  132.         return styles.getAttributeNames();
  133.     }
  134.  
  135.     /**
  136.      * Adds a listener to track when styles are added
  137.      * or removed.
  138.      *
  139.      * @param l the change listener
  140.      */
  141.     public void addChangeListener(ChangeListener l) {
  142.         styles.addChangeListener(l);
  143.     }
  144.  
  145.     /**
  146.      * Removes a listener that was tracking styles being
  147.      * added or removed.
  148.      *
  149.      * @param l the change listener
  150.      */
  151.     public void removeChangeListener(ChangeListener l) {
  152.         styles.removeChangeListener(l);
  153.     }
  154.  
  155.     /**
  156.      * Gets the font from an attribute set.  This is
  157.      * implemented to try and fetch a cached font
  158.      * for the given AttributeSet, and if that fails 
  159.      * the font features are resolved and the
  160.      * font is fetched from the low-level font cache.
  161.      *
  162.      * @param attr the attribute set
  163.      * @return the font
  164.      */
  165.     public Font getFont(AttributeSet attr) {
  166.         // PENDING(prinz) add cache behavior
  167.         int style = Font.PLAIN;
  168.         if (StyleConstants.isBold(attr)) {
  169.             style |= Font.BOLD;
  170.         }
  171.         if (StyleConstants.isItalic(attr)) {
  172.             style |= Font.ITALIC;
  173.         }
  174.         String family = StyleConstants.getFontFamily(attr);
  175.         int size = StyleConstants.getFontSize(attr);
  176.         return getFont(family, style, size);
  177.     }
  178.  
  179.     /**
  180.      * Gets a new font.  This returns a Font from a cache
  181.      * if a cached font exists.  If not, a Font is added to
  182.      * the cache.  This is basically a low-level cache for
  183.      * 1.1 font features.
  184.      *
  185.      * @param family the font family (such as "Monospaced")
  186.      * @param style the style of the font (such as Font.PLAIN)
  187.      * @param size the point size >= 1
  188.      * @return the new font
  189.      */
  190.     public Font getFont(String family, int style, int size) {
  191.         fontSearch.setValue(family, style, size);
  192.         Font f = (Font) fontTable.get(fontSearch);
  193.         if (f == null) {
  194.             // haven't seen this one yet.
  195.             f = new Font(family, style, size);
  196.             FontKey key = new FontKey(family, style, size);
  197.             fontTable.put(key, f);
  198.         }
  199.         return f;
  200.     }
  201.  
  202.     /**
  203.      * Returns font metrics for a font.
  204.      *
  205.      * @param f the font
  206.      * @return the metrics
  207.      */
  208.     public FontMetrics getFontMetrics(Font f) {
  209.         // The Toolkit implementations cache, so we just forward
  210.         // to the default toolkit.
  211.         return Toolkit.getDefaultToolkit().getFontMetrics(f);
  212.     }
  213.  
  214.     // --- AttributeContext methods --------------------
  215.  
  216.     /**
  217.      * Adds an attribute to the given set, and returns
  218.      * the new representative set.
  219.      * <p>
  220.      * This method is thread safe, although most Swing methods
  221.      * are not. Please see 
  222.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  223.      * and Swing</A> for more information.     
  224.      *
  225.      * @param old the old attribute set
  226.      * @param name the non-null attribute name
  227.      * @param value the attribute value
  228.      * @return the updated attribute set
  229.      * @see MutableAttributeSet#addAttribute
  230.      */
  231.     public synchronized AttributeSet addAttribute(AttributeSet old, Object name, Object value) {
  232.         if ((old.getAttributeCount() + 1) <= getCompressionThreshold()) {
  233.             // build a search key and find/create an immutable and unique
  234.             // set.
  235.             search.removeAttributes(search);
  236.             search.addAttributes(old);
  237.             search.addAttribute(name, value);
  238.             reclaim(old);
  239.             return getImmutableUniqueSet();
  240.         }
  241.         MutableAttributeSet ma = getMutableAttributeSet(old);
  242.         ma.addAttribute(name, value);
  243.         return ma;
  244.     }
  245.  
  246.     /**
  247.      * Adds a set of attributes to the element.
  248.      * <p>
  249.      * This method is thread safe, although most Swing methods
  250.      * are not. Please see 
  251.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  252.      * and Swing</A> for more information.     
  253.      *
  254.      * @param old the old attribute set
  255.      * @param attr the attributes to add
  256.      * @return the updated attribute set
  257.      * @see MutableAttributeSet#addAttribute
  258.      */
  259.     public synchronized AttributeSet addAttributes(AttributeSet old, AttributeSet attr) {
  260.         if ((old.getAttributeCount() + attr.getAttributeCount()) <= getCompressionThreshold()) {
  261.             // build a search key and find/create an immutable and unique
  262.             // set.
  263.             search.removeAttributes(search);
  264.             search.addAttributes(old);
  265.             search.addAttributes(attr);
  266.             reclaim(old);
  267.             return getImmutableUniqueSet();
  268.         }
  269.         MutableAttributeSet ma = getMutableAttributeSet(old);
  270.         ma.addAttributes(attr);
  271.         return ma;
  272.     }
  273.  
  274.     /**
  275.      * Removes an attribute from the set.
  276.      * <p>
  277.      * This method is thread safe, although most Swing methods
  278.      * are not. Please see 
  279.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  280.      * and Swing</A> for more information.     
  281.      *
  282.      * @param old the old set of attributes
  283.      * @param name the non-null attribute name
  284.      * @return the updated attribute set
  285.      * @see MutableAttributeSet#removeAttribute
  286.      */
  287.     public synchronized AttributeSet removeAttribute(AttributeSet old, Object name) {
  288.         if ((old.getAttributeCount() - 1) <= getCompressionThreshold()) {
  289.             // build a search key and find/create an immutable and unique
  290.             // set.
  291.             search.removeAttributes(search);
  292.             search.addAttributes(old);
  293.             search.removeAttribute(name);
  294.             reclaim(old);
  295.             return getImmutableUniqueSet();
  296.         }
  297.         MutableAttributeSet ma = getMutableAttributeSet(old);
  298.         ma.removeAttribute(name);
  299.         return ma;
  300.     }
  301.  
  302.     /**
  303.      * Removes a set of attributes for the element.
  304.      * <p>
  305.      * This method is thread safe, although most Swing methods
  306.      * are not. Please see 
  307.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  308.      * and Swing</A> for more information.     
  309.      *
  310.      * @param old the old attribute set
  311.      * @param names the attribute names
  312.      * @return the updated attribute set
  313.      * @see MutableAttributeSet#removeAttributes
  314.      */
  315.     public synchronized AttributeSet removeAttributes(AttributeSet old, Enumeration names) {
  316.         if (old.getAttributeCount() <= getCompressionThreshold()) {
  317.             // build a search key and find/create an immutable and unique
  318.             // set.
  319.             search.removeAttributes(search);
  320.             search.addAttributes(old);
  321.             search.removeAttributes(names);
  322.             reclaim(old);
  323.             return getImmutableUniqueSet();
  324.         }
  325.         MutableAttributeSet ma = getMutableAttributeSet(old);
  326.         ma.removeAttributes(names);
  327.         return ma;
  328.     }
  329.  
  330.     /**
  331.      * Removes a set of attributes for the element.
  332.      * <p>
  333.      * This method is thread safe, although most Swing methods
  334.      * are not. Please see 
  335.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  336.      * and Swing</A> for more information.     
  337.      *
  338.      * @param old the old attribute set
  339.      * @param attrs the attributes
  340.      * @return the updated attribute set
  341.      * @see MutableAttributeSet#removeAttributes
  342.      */
  343.     public synchronized AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs) {
  344.         if (old.getAttributeCount() <= getCompressionThreshold()) {
  345.             // build a search key and find/create an immutable and unique
  346.             // set.
  347.             search.removeAttributes(search);
  348.             search.addAttributes(old);
  349.             search.removeAttributes(attrs);
  350.             reclaim(old);
  351.             return getImmutableUniqueSet();
  352.         }
  353.         MutableAttributeSet ma = getMutableAttributeSet(old);
  354.         ma.removeAttributes(attrs);
  355.         return ma;
  356.     }
  357.  
  358.     /**
  359.      * Fetches an empty AttributeSet.
  360.      *
  361.      * @return the set
  362.      */
  363.     public AttributeSet getEmptySet() {
  364.         return SimpleAttributeSet.EMPTY;
  365.     }
  366.  
  367.     /**
  368.      * Returns a set no longer needed by the MutableAttributeSet implmentation.
  369.      * This is useful for operation under 1.1 where there are no weak 
  370.      * references.  This would typically be called by the finalize method
  371.      * of the MutableAttributeSet implementation.
  372.      * <p>
  373.      * This method is thread safe, although most Swing methods
  374.      * are not. Please see 
  375.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  376.      * and Swing</A> for more information.     
  377.      *
  378.      * @param a the set to reclaim
  379.      */
  380.     public void reclaim(AttributeSet a) {
  381.         if (a instanceof SmallAttributeSet) {
  382.             SmallAttributeSet sa = (SmallAttributeSet) a;
  383.             sa.nrefs -= 1;
  384.             if (sa.nrefs <= 0) {
  385.                 unusedSets += 1;
  386.                 if ((unusedSets > 10) && (unusedSets > (attributesPool.size() / 10))) {
  387.                     if (SwingUtilities.isEventDispatchThread()) {
  388.                         removeUnusedSets();
  389.                     } else {
  390.                         Runnable callRemoveUnused = new Runnable() {
  391.                             public void run() {
  392.                                 removeUnusedSets();
  393.                             }
  394.                         };
  395.                         SwingUtilities.invokeLater(callRemoveUnused);
  396.                     }
  397.                 }
  398.             }
  399.         }
  400.     }
  401.  
  402.     // --- local methods -----------------------------------------------
  403.  
  404.     /**
  405.      * Returns the maximum number of key/value pairs to try and 
  406.      * compress into unique/immutable sets.  Any sets above this
  407.      * limit will use hashtables and be a MutableAttributeSet.
  408.      *
  409.      * @return the threshold
  410.      */
  411.     protected int getCompressionThreshold() {
  412.         return THRESHOLD;
  413.     }
  414.  
  415.     /**
  416.      * Clean the unused immutable sets out of the hashtable.
  417.      */
  418.     synchronized void removeUnusedSets() {
  419.         Vector rmList = new Vector();
  420.         Enumeration sets = attributesPool.keys();
  421.         while (sets.hasMoreElements()) {
  422.             SmallAttributeSet set = (SmallAttributeSet)sets.nextElement();
  423.             if (set.nrefs <= 0) {
  424.                 rmList.addElement(set);
  425.             }
  426.         }
  427.         sets = rmList.elements();
  428.         while (sets.hasMoreElements()) {
  429.             attributesPool.remove(sets.nextElement());
  430.         }
  431.         unusedSets = 0;
  432.     }
  433.  
  434.     /**
  435.      * Search for an existing attribute set using the current search
  436.      * parameters.  If a matching set is found, return it.  If a match
  437.      * is not found, we create a new set and add it to the pool.
  438.      */
  439.     AttributeSet getImmutableUniqueSet() {
  440.     // PENDING(prinz) should consider finding a alternative to
  441.     // generating extra garbage on search key.
  442.     SmallAttributeSet key = new SmallAttributeSet(search);
  443.         SmallAttributeSet a = (SmallAttributeSet) attributesPool.get(key);
  444.         if (a == null) {
  445.             a = key;
  446.             attributesPool.put(a, a);
  447.         }
  448.         a.nrefs += 1;
  449.         return a;
  450.     }
  451.  
  452.     /**
  453.      * Creates a mutable attribute set to hand out because the current
  454.      * needs are too big to try and use a shared version.
  455.      */
  456.     MutableAttributeSet getMutableAttributeSet(AttributeSet a) {
  457.         if (a instanceof MutableAttributeSet) {
  458.             return (MutableAttributeSet) a;
  459.         }
  460.         return new SimpleAttributeSet(a);
  461.     }
  462.  
  463.     /**
  464.      * Converts a StyleContext to a String.
  465.      *
  466.      * @return the string
  467.      */
  468.     public String toString() {
  469.         removeUnusedSets();
  470.         String s = "";
  471.         Enumeration sets = attributesPool.keys();
  472.         while (sets.hasMoreElements()) {
  473.             SmallAttributeSet set = (SmallAttributeSet)sets.nextElement();
  474.             s = s + set + "\n";
  475.         }
  476.         return s;
  477.     }
  478.  
  479.     // --- serialization ---------------------------------------------
  480.  
  481.     /**
  482.      * Writes a set of attributes to the given object stream
  483.      * for the purpose of serialization.  This will take
  484.      * special care to deal with static attribute keys that
  485.      * have been registered wit the 
  486.      * <code>registerStaticAttributeKey</code> method.
  487.      * Any attribute key not regsitered as a static key
  488.      * will be serialized directly.  All values are expected
  489.      * to be serializable.
  490.      *
  491.      * @param out the output stream
  492.      * @param a the attribute set
  493.      * @exception IOException on any I/O error
  494.      */
  495.     public static void writeAttributeSet(ObjectOutputStream out, 
  496.                                          AttributeSet a) throws IOException {
  497.         int n = a.getAttributeCount();
  498.         out.writeInt(n);
  499.         Enumeration keys = a.getAttributeNames();
  500.         while (keys.hasMoreElements()) {
  501.             Object key = keys.nextElement();
  502.             if (key instanceof Serializable) {
  503.                 out.writeObject(key);
  504.             } else {
  505.                 Object ioFmt = freezeKeyMap.get(key);
  506.                 out.writeObject(ioFmt);
  507.             }
  508.             Object value = a.getAttribute(key);
  509.             out.writeObject(value);
  510.         }
  511.     }
  512.  
  513.     /**
  514.      * Reads a set of attributes from the given object input
  515.      * stream that have been previously written out with
  516.      * <code>writeAttributeSet</code>.  This will try to restore
  517.      * keys that were static objects to the static objects in
  518.      * the current virtual machine considering only those keys
  519.      * that have been registered with the 
  520.      * <code>registerStaticAttributeKey</code> method.
  521.      * The attributes retrieved from the stream will be placed
  522.      * into the given mutable set.
  523.      *
  524.      * @param in the object stream to read the attribute data from.
  525.      * @param a  the attribute set to place the attribute 
  526.      *   definitions in.
  527.      * @exception ClassNotFoundException passed upward if encountered
  528.      *  when reading the object stream.
  529.      * @exception IOException passed upward if encountered when
  530.      *  reading the object stream.
  531.      */
  532.     public static void readAttributeSet(ObjectInputStream in, 
  533.         MutableAttributeSet a) throws ClassNotFoundException, IOException {
  534.  
  535.         int n = in.readInt();
  536.         for (int i = 0; i < n; i++) {
  537.             Object key = in.readObject();
  538.             Object value = in.readObject();
  539.             Object staticKey = thawKeyMap.get(key);
  540.             if (staticKey != null) {
  541.                 key = staticKey;
  542.             }
  543.             a.addAttribute(key, value);
  544.         }
  545.     }
  546.  
  547.     /**
  548.      * Registers an object as a static object that is being
  549.      * used as a key in attribute sets.  This allows the key
  550.      * to be treated specially for serialization.
  551.      * <p>
  552.      * For operation under a 1.1 virtual machine, this
  553.      * uses the value returned by <code>toString</code>
  554.      * concatenated to the classname.  The value returned
  555.      * by toString should not have the class reference
  556.      * in it (ie it should be reimplemented from the 
  557.      * definition in Object) in order to be the same when
  558.      * recomputed later.
  559.      *
  560.      * @param key the non-null object key
  561.      */
  562.     public static void registerStaticAttributeKey(Object key) {
  563.         String ioFmt = key.getClass().getName() + "." + key.toString();
  564.         if (freezeKeyMap == null) {
  565.             freezeKeyMap = new Hashtable();
  566.             thawKeyMap = new Hashtable();
  567.         }
  568.         freezeKeyMap.put(key, ioFmt);
  569.         thawKeyMap.put(ioFmt, key);
  570.     }
  571.  
  572.     private void writeObject(java.io.ObjectOutputStream s)
  573.         throws IOException
  574.     {
  575.         // clean out unused sets before saving
  576.         removeUnusedSets();
  577.  
  578.         s.defaultWriteObject();
  579.     }
  580.  
  581.     private void readObject(ObjectInputStream s)
  582.       throws ClassNotFoundException, IOException 
  583.     {
  584.         fontSearch = new FontKey(null, 0, 0);
  585.         fontTable = new Hashtable();
  586.         search = new SimpleAttributeSet();
  587.         attributesPool = new Hashtable();
  588.         s.defaultReadObject();
  589.     }
  590.  
  591.     // --- variables ---------------------------------------------------
  592.  
  593.     /**
  594.      * The name given to the default logical style attached
  595.      * to paragraphs.
  596.      */
  597.     public static final String DEFAULT_STYLE = "default";
  598.  
  599.     private static Hashtable freezeKeyMap;
  600.     private static Hashtable thawKeyMap;
  601.  
  602.     private Style styles;
  603.     private transient FontKey fontSearch = new FontKey(null, 0, 0);
  604.     private transient Hashtable fontTable = new Hashtable();
  605.  
  606.     private transient Hashtable attributesPool = new Hashtable();
  607.     private transient MutableAttributeSet search = new SimpleAttributeSet();
  608.  
  609.     /**
  610.      * Number of immutable sets that are not currently
  611.      * being used.  This helps indicate when the sets need
  612.      * to be cleaned out of the hashtable they are stored
  613.      * in.
  614.      */
  615.     private int unusedSets;
  616.  
  617.     /**
  618.      * The threshold for no longer sharing the set of attributes
  619.      * in an immutable table.
  620.      */
  621.     static final int THRESHOLD = 9;
  622.     
  623.     /**
  624.      * This holds a small number of attributes in an array.
  625.      * The keys are held as strings interned locally
  626.      * in the string pool so they can be compared by comparing
  627.      * references.  The strings are also sorted lexically so 
  628.      * that comparisons for equality are quick, as probes
  629.      * for existing sets happen frequently.  The storage 
  630.      * format is key, value, key, value, etc.  The size
  631.      * of the set is the length of the array divided by
  632.      * two.
  633.      */
  634.     class SmallAttributeSet implements AttributeSet {
  635.  
  636.         SmallAttributeSet(Object[] attributes) {
  637.             this.attributes = attributes;
  638.         }
  639.  
  640.         SmallAttributeSet(AttributeSet attrs) {
  641.             int n = attrs.getAttributeCount();
  642.             Object[] tbl = new Object[2 * n];
  643.             Enumeration names = attrs.getAttributeNames();
  644.             int i = 0;
  645.             while (names.hasMoreElements()) {
  646.                 tbl[i] = names.nextElement();
  647.                 tbl[i+1] = attrs.getAttribute(tbl[i]);
  648.                 i += 2;
  649.             }
  650.             attributes = tbl;
  651.         }
  652.  
  653.         Object getLocalAttribute(Object nm) {
  654.             Object[] tbl = attributes;
  655.             for (int i = 0; i < tbl.length; i += 2) {
  656.                 if (nm.equals(tbl[i])) {
  657.                     return tbl[i+1];
  658.                 }
  659.             }
  660.             return null;
  661.         }
  662.  
  663.         // --- Object methods -------------------------
  664.  
  665.         /**
  666.          * Returns a string showing the key/value pairs
  667.          */
  668.         public String toString() {
  669.             String s = "{";
  670.             Object[] tbl = attributes;
  671.             for (int i = 0; i < tbl.length; i += 2) {
  672.                 if (tbl[i+1] instanceof AttributeSet) {
  673.                     // don't recurse
  674.                     s = s + tbl[i] + "=" + "AttributeSet" + ",";
  675.                 } else {
  676.                     s = s + tbl[i] + "=" + tbl[i+1] + ",";
  677.                 }
  678.             }
  679.             s = s + "nrefs=" + nrefs + "}";
  680.             return s;
  681.         }
  682.  
  683.         /**
  684.          * Returns a hashcode for this set of attributes.
  685.          * @return     a hashcode value for this set of attributes.
  686.          */
  687.         public int hashCode() {
  688.             int code = 0;
  689.             Object[] tbl = attributes;
  690.             for (int i = 1; i < tbl.length; i += 2) {
  691.                 code ^= tbl[i].hashCode();
  692.             }
  693.             return code;
  694.         }
  695.  
  696.         /**
  697.          * Compares this object to the specifed object.
  698.          * The result is <code>true</code> if and only if the argument is not 
  699.          * <code>null</code> and is a <code>Font</code> object with the same 
  700.          * name, style, and point size as this font. 
  701.          * @param     obj   the object to compare this font with.
  702.          * @return    <code>true</code> if the objects are equal; 
  703.          *            <code>false</code> otherwise.
  704.          */
  705.         public boolean equals(Object obj) {
  706.             if (obj instanceof AttributeSet) {
  707.                 AttributeSet attrs = (AttributeSet) obj;
  708.                 return ((getAttributeCount() == attrs.getAttributeCount()) &&
  709.                         containsAttributes(attrs));
  710. /*
  711.                 Object[] left = attributes;
  712.                 Object[] right = ((SmallAttributeSet)obj).attributes;
  713.                 if (left.length == right.length) {
  714.                     // compare the two tables... since the keys are interned
  715.                     // we can compare references on those
  716.                     // for speed, and call Object.equals on the associated value.
  717.                     int n = left.length;
  718.                     for (int i = 0; i < n; i += 2) {
  719.                         if (left[i] != right[i]) {
  720.                             return false;
  721.                         }
  722.                     }
  723.                     for (int i = 1; i < n; i += 2) {
  724.                         if (! left[i].equals(right[i])) {
  725.                             return false;
  726.                         }
  727.                     }
  728.                     return true;
  729.                 }
  730.                 */
  731.             }
  732.             return false;
  733.         }
  734.  
  735.         //  --- AttributeSet methods ----------------------------
  736.  
  737.         /**
  738.          * Gets the number of attributes that are defined.
  739.          *
  740.          * @return the number of attributes
  741.          * @see AttributeSet#getAttributeCount
  742.          */
  743.         public int getAttributeCount() {
  744.             return attributes.length / 2;
  745.         }
  746.  
  747.         /**
  748.          * Checks whether a given attribute is defined.
  749.          *
  750.          * @param key the attribute key
  751.          * @return true if the attribute is defined
  752.          * @see AttributeSet#isDefined
  753.          */
  754.         public boolean isDefined(Object key) {
  755.             Object[] a = attributes;
  756.             int n = a.length;
  757.             for (int i = 0; i < n; i += 2) {
  758.                 if (key.equals(a[i])) {
  759.                     return true;
  760.                 }
  761.             }
  762.             return false;
  763.         }
  764.  
  765.         /**
  766.          * Checks whether two attribute sets are equal.
  767.          *
  768.          * @param attr the attribute set to check against
  769.          * @return true if the same
  770.          * @see AttributeSet#isEqual
  771.          */
  772.         public boolean isEqual(AttributeSet attr) {
  773.             if (attr instanceof SmallAttributeSet) {
  774.                 return attr == this;
  775.             }
  776.             return ((getAttributeCount() == attr.getAttributeCount()) &&
  777.                     containsAttributes(attr));
  778.         }
  779.  
  780.         /**
  781.          * Copies a set of attributes.
  782.          *
  783.          * @return the copy
  784.          * @see AttributeSet#copyAttributes
  785.          */
  786.         public AttributeSet copyAttributes() {
  787.             return this;
  788.         }
  789.  
  790.         /**
  791.          * Gets the value of an attribute.
  792.          *
  793.          * @param key the attribute name
  794.          * @return the attribute value
  795.          * @see AttributeSet#getAttribute
  796.          */
  797.         public Object getAttribute(Object key) {
  798.             Object value = getLocalAttribute(key);
  799.             if (value == null) {
  800.                 AttributeSet parent = getResolveParent();
  801.                 if (parent != null)
  802.                     value = parent.getAttribute(key);
  803.             }
  804.             return value;
  805.         }
  806.  
  807.         /**
  808.          * Gets the names of all attributes.
  809.          *
  810.          * @return the attribute names
  811.          * @see AttributeSet#getAttributeNames
  812.          */
  813.         public Enumeration getAttributeNames() {
  814.             return new KeyEnumeration(attributes);
  815.         }
  816.  
  817.         /**
  818.          * Checks whether a given attribute name/value is defined.
  819.          *
  820.          * @param name the attribute name
  821.          * @param value the attribute value
  822.          * @return true if the name/value is defined
  823.          * @see AttributeSet#containsAttribute
  824.          */
  825.         public boolean containsAttribute(Object name, Object value) {
  826.             return value.equals(getAttribute(name));
  827.         }
  828.  
  829.         /**
  830.          * Checks whether the attribute set contains all of
  831.          * the given attributes.
  832.          *
  833.          * @param attrs the attributes to check
  834.          * @return true if the element contains all the attributes
  835.          * @see AttributeSet#containsAttributes
  836.          */
  837.         public boolean containsAttributes(AttributeSet attrs) {
  838.             boolean result = true;
  839.  
  840.             Enumeration names = attrs.getAttributeNames();
  841.             while (result && names.hasMoreElements()) {
  842.                 Object name = names.nextElement();
  843.                 result = attrs.getAttribute(name).equals(getAttribute(name));
  844.             }
  845.  
  846.             return result;
  847.         }
  848.  
  849.         /**
  850.          * If not overriden, the resolving parent defaults to 
  851.          * the parent element.
  852.          *
  853.          * @return the attributes from the parent
  854.          * @see AttributeSet#getResolveParent
  855.          */
  856.         public AttributeSet getResolveParent() {
  857.             return (AttributeSet) getLocalAttribute(StyleConstants.ResolveAttribute);
  858.         }
  859.  
  860.         /**
  861.          * Clones a set of attributes.  Since the set is immutable, a
  862.          * clone is basically the same set.
  863.          *
  864.          * @return the set of attributes
  865.          */
  866.         public Object clone() {
  867.             return this;
  868.         }
  869.  
  870.         // --- variables -----------------------------------------
  871.  
  872.         Object[] attributes;
  873.         int nrefs;
  874.     }
  875.  
  876.     /**
  877.      * An enumeration of the keys in a SmallAttributeSet.
  878.      */
  879.     class KeyEnumeration implements Enumeration {
  880.  
  881.         KeyEnumeration(Object[] attr) {
  882.             this.attr = attr;
  883.             i = 0;
  884.         }
  885.  
  886.         /**
  887.          * Tests if this enumeration contains more elements.
  888.          *
  889.          * @return  <code>true</code> if this enumeration contains more elements;
  890.          *          <code>false</code> otherwise.
  891.          * @since   JDK1.0
  892.          */
  893.         public boolean hasMoreElements() {
  894.             return i < attr.length;
  895.         }
  896.  
  897.         /**
  898.          * Returns the next element of this enumeration.
  899.          *
  900.          * @return     the next element of this enumeration. 
  901.          * @exception  NoSuchElementException  if no more elements exist.
  902.          * @since      JDK1.0
  903.          */
  904.         public Object nextElement() {
  905.             if (i < attr.length) {
  906.                 Object o = attr[i];
  907.                 i += 2;
  908.                 return o;
  909.             }
  910.             throw new NoSuchElementException();
  911.         }
  912.  
  913.         Object[] attr;
  914.         int i;
  915.     }
  916.  
  917.     /**
  918.      * Sorts the key strings so that they can be very quickly compared
  919.      * in the attribute set searchs.
  920.      */
  921.     class KeyBuilder {
  922.  
  923.         public void initialize(AttributeSet a) {
  924.             if (a instanceof SmallAttributeSet) {
  925.                 initialize(((SmallAttributeSet)a).attributes);
  926.             } else {
  927.                 keys.removeAllElements();
  928.                 data.removeAllElements();
  929.                 Enumeration names = a.getAttributeNames();
  930.                 while (names.hasMoreElements()) {
  931.                     Object name = names.nextElement();
  932.                     addAttribute(name, a.getAttribute(name));
  933.                 }
  934.             }
  935.         }
  936.  
  937.         /**
  938.          * Initialize with a set of already sorted
  939.          * keys (data from an existing SmallAttributeSet).
  940.          */
  941.         private void initialize(Object[] sorted) {
  942.             keys.removeAllElements();
  943.             data.removeAllElements();
  944.             int n = sorted.length;
  945.             for (int i = 0; i < n; i += 2) {
  946.                 keys.addElement(sorted[i]);
  947.                 data.addElement(sorted[i+1]);
  948.             }
  949.         }
  950.  
  951.         /**
  952.          * Creates a table of sorted key/value entries
  953.          * suitable for creation of an instance of 
  954.          * SmallAttributeSet.
  955.          */
  956.         public Object[] createTable() {
  957.             int n = keys.size();
  958.             Object[] tbl = new Object[2 * n];
  959.             for (int i = 0; i < n; i ++) {
  960.                 int offs = 2 * i;
  961.                 tbl[offs] = keys.elementAt(i);
  962.                 tbl[offs + 1] = data.elementAt(i);
  963.             }
  964.             return tbl;
  965.         }
  966.  
  967.         /**
  968.          * The number of key/value pairs contained
  969.          * in the current key being forged.
  970.          */
  971.         int getCount() {
  972.             return keys.size();
  973.         }
  974.  
  975.         /**
  976.          * Adds a key/value to the set.
  977.          */
  978.         public void addAttribute(Object key, Object value) {
  979.             keys.addElement(key);
  980.             data.addElement(value);
  981.         }
  982.  
  983.         /**
  984.          * Adds a set of key/value pairs to the set.
  985.          */
  986.         public void addAttributes(AttributeSet attr) {
  987.             if (attr instanceof SmallAttributeSet) {
  988.                 // avoid searching the keys, they are already interned.
  989.                 Object[] tbl = ((SmallAttributeSet)attr).attributes;
  990.                 int n = tbl.length;
  991.                 for (int i = 0; i < n; i += 2) {
  992.                     addAttribute(tbl[i], tbl[i+1]);
  993.                 }
  994.             } else {
  995.                 Enumeration names = attr.getAttributeNames();
  996.                 while (names.hasMoreElements()) {
  997.                     Object name = names.nextElement();
  998.                     addAttribute(name, attr.getAttribute(name));
  999.                 }
  1000.             }
  1001.         }
  1002.  
  1003.         /**
  1004.          * Removes the given name from the set.
  1005.          */
  1006.         public void removeAttribute(Object key) {
  1007.             int n = keys.size();
  1008.             for (int i = 0; i < n; i++) {
  1009.                 if (keys.elementAt(i).equals(key)) {
  1010.                     keys.removeElementAt(i);
  1011.                     data.removeElementAt(i);
  1012.                     return;
  1013.                 }
  1014.             }
  1015.         }
  1016.  
  1017.         /**
  1018.          * Removes the set of keys from the set.
  1019.          */
  1020.         public void removeAttributes(Enumeration names) {
  1021.             while (names.hasMoreElements()) {
  1022.                 Object name = names.nextElement();
  1023.                 removeAttribute(name);
  1024.             }
  1025.         }
  1026.  
  1027.         /**
  1028.          * Removes the set of matching attributes from the set.
  1029.          */
  1030.         public void removeAttributes(AttributeSet attr) {
  1031.             Enumeration names = attr.getAttributeNames();
  1032.             while (names.hasMoreElements()) {
  1033.                 Object name = names.nextElement();
  1034.                 Object value = attr.getAttribute(name);
  1035.                 removeSearchAttribute(name, value);
  1036.             }
  1037.         }
  1038.  
  1039.         private void removeSearchAttribute(Object ikey, Object value) {
  1040.             int n = keys.size();
  1041.             for (int i = 0; i < n; i++) {
  1042.                 if (keys.elementAt(i).equals(ikey)) {
  1043.                     if (data.elementAt(i).equals(value)) {
  1044.                         keys.removeElementAt(i);
  1045.                         data.removeElementAt(i);
  1046.                     }
  1047.                     return;
  1048.                 }
  1049.             }
  1050.         }
  1051.  
  1052.         private Vector keys = new Vector();
  1053.         private Vector data = new Vector();
  1054.     }
  1055.  
  1056.     /**
  1057.      * key for a font table
  1058.      */
  1059.     static class FontKey {
  1060.  
  1061.         private String family;
  1062.         private int style;
  1063.         private int size;
  1064.  
  1065.         /**
  1066.          * Constructs a font key.
  1067.          */
  1068.         public FontKey(String family, int style, int size) {
  1069.             setValue(family, style, size);
  1070.         }
  1071.  
  1072.         public void setValue(String family, int style, int size) {
  1073.             this.family = (family != null) ? family.intern() : null;
  1074.             this.style = style;
  1075.             this.size = size;
  1076.         }
  1077.  
  1078.         /**
  1079.          * Returns a hashcode for this font.
  1080.          * @return     a hashcode value for this font.
  1081.          */
  1082.         public int hashCode() {
  1083.             return family.hashCode() ^ style ^ size;
  1084.         }
  1085.     
  1086.         /**
  1087.          * Compares this object to the specifed object.
  1088.          * The result is <code>true</code> if and only if the argument is not 
  1089.          * <code>null</code> and is a <code>Font</code> object with the same 
  1090.          * name, style, and point size as this font. 
  1091.          * @param     obj   the object to compare this font with.
  1092.          * @return    <code>true</code> if the objects are equal; 
  1093.          *            <code>false</code> otherwise.
  1094.          */
  1095.         public boolean equals(Object obj) {
  1096.             if (obj instanceof FontKey) {
  1097.                 FontKey font = (FontKey)obj;
  1098.                 return (size == font.size) && (style == font.style) && (family == font.family);
  1099.             }
  1100.             return false;
  1101.         }
  1102.  
  1103.     }
  1104.  
  1105.     /**
  1106.      * A collection of attributes, typically used to represent
  1107.      * character and paragraph styles.  This is an implementation
  1108.      * of MutableAttributeSet that can be observed if desired.
  1109.      * These styles will take advantage of immutability while
  1110.      * the sets are small enough, and may be substantially more
  1111.      * efficient than something like SimpleAttributeSet.
  1112.      * <p>
  1113.      * Warning: serialized objects of this class will not be compatible with
  1114.      * future swing releases.  The current serialization support is appropriate
  1115.      * for short term storage or RMI between Swing1.0 applications.  It will
  1116.      * not be possible to load serialized Swing1.0 objects with future releases
  1117.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  1118.      * baseline for the serialized form of Swing objects.
  1119.      */
  1120.     public class NamedStyle implements Style, Serializable {
  1121.  
  1122.         /**
  1123.          * Creates a new named style.
  1124.          *
  1125.          * @param name the style name, null for unnamed
  1126.          * @param parent the parent style, null if none
  1127.          */
  1128.         public NamedStyle(String name, Style parent) {
  1129.             attributes = getEmptySet();
  1130.             if (name != null) {
  1131.                 setName(name);
  1132.             }
  1133.             if (parent != null) {
  1134.                 setResolveParent(parent);
  1135.             }
  1136.         }
  1137.  
  1138.         /**
  1139.          * Creates a new named style.
  1140.          *
  1141.          * @param parent the parent style, null if none
  1142.          */
  1143.         public NamedStyle(Style parent) {
  1144.             this(null, parent);
  1145.         }
  1146.  
  1147.         /**
  1148.          * Creates a new named style, with a null name and parent.
  1149.          */
  1150.         public NamedStyle() {
  1151.             attributes = getEmptySet();
  1152.         }
  1153.  
  1154.         /**
  1155.          * Converts the style to a string.
  1156.          *
  1157.          * @return the string
  1158.          */
  1159.         public String toString() {
  1160.             return "NamedStyle:" + getName() + " " + attributes;
  1161.         }
  1162.  
  1163.         /**
  1164.          * Fetches the name of the style.   A style is not required to be named,
  1165.          * so null is returned if there is no name associated with the style.
  1166.          *
  1167.          * @return the name
  1168.          */
  1169.         public String getName() {
  1170.             if (isDefined(StyleConstants.NameAttribute)) {
  1171.                 return (String) getAttribute(StyleConstants.NameAttribute);
  1172.             }
  1173.             return null;
  1174.         }
  1175.  
  1176.         /**
  1177.          * Changes the name of the style.  Does nothing with a null name.
  1178.          *
  1179.          * @param name the new name
  1180.          */
  1181.         public void setName(String name) {
  1182.             if (name != null) {
  1183.                 this.addAttribute(StyleConstants.NameAttribute, name);
  1184.             }
  1185.         }
  1186.  
  1187.         /**
  1188.          * Adds a change listener.
  1189.          *
  1190.          * @param l the change listener
  1191.          * @see Attributes#addChangeListener
  1192.          */
  1193.         public void addChangeListener(ChangeListener l) {
  1194.             listenerList.add(ChangeListener.class, l);
  1195.         }
  1196.  
  1197.         /**
  1198.          * Removes a change listener.
  1199.          *
  1200.          * @param l the change listener
  1201.          * @see Attributes#removeChangeListener
  1202.          */
  1203.         public void removeChangeListener(ChangeListener l) {
  1204.             listenerList.remove(ChangeListener.class, l);
  1205.         }
  1206.  
  1207.  
  1208.         /**
  1209.          * Notifies all listeners that have registered interest for
  1210.          * notification on this event type.  The event instance 
  1211.          * is lazily created using the parameters passed into 
  1212.          * the fire method.
  1213.          *
  1214.          * @see EventListenerList
  1215.          */
  1216.         protected void fireStateChanged() {
  1217.             // Guaranteed to return a non-null array
  1218.             Object[] listeners = listenerList.getListenerList();
  1219.             // Process the listeners last to first, notifying
  1220.             // those that are interested in this event
  1221.             for (int i = listeners.length-2; i>=0; i-=2) {
  1222.                 if (listeners[i]==ChangeListener.class) {
  1223.                     // Lazily create the event:
  1224.                     if (changeEvent == null)
  1225.                         changeEvent = new ChangeEvent(this);
  1226.                     ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  1227.                 }              
  1228.             }
  1229.         }       
  1230.         
  1231.         // --- AttributeSet ----------------------------
  1232.         // delegated to the immutable field "attributes"
  1233.  
  1234.         /**
  1235.          * Gets the number of attributes that are defined.
  1236.          *
  1237.          * @return the number of attributes >= 0
  1238.          * @see AttributeSet#getAttributeCount
  1239.          */
  1240.         public int getAttributeCount() {
  1241.             return attributes.getAttributeCount();
  1242.         }
  1243.  
  1244.         /**
  1245.          * Checks whether a given attribute is defined.
  1246.          *
  1247.          * @param attrName the non-null attribute name
  1248.          * @return true if the attribute is defined
  1249.          * @see AttributeSet#isDefined
  1250.          */
  1251.         public boolean isDefined(Object attrName) {
  1252.             return attributes.isDefined(attrName);
  1253.         }
  1254.  
  1255.         /**
  1256.          * Checks whether two attribute sets are equal.
  1257.          *
  1258.          * @param attr the attribute set to check against
  1259.          * @return true if the same
  1260.          * @see AttributeSet#isEqual
  1261.          */
  1262.         public boolean isEqual(AttributeSet attr) {
  1263.             return attributes.isEqual(attr);
  1264.         }
  1265.  
  1266.         /**
  1267.          * Copies a set of attributes.
  1268.          *
  1269.          * @return the copy
  1270.          * @see AttributeSet#copyAttributes
  1271.          */
  1272.         public AttributeSet copyAttributes() {
  1273.             NamedStyle a = new NamedStyle();
  1274.             a.attributes = attributes.copyAttributes();
  1275.             return a;
  1276.         }
  1277.  
  1278.         /**
  1279.          * Gets the value of an attribute.
  1280.          *
  1281.          * @param attrName the non-null attribute name
  1282.          * @return the attribute value
  1283.          * @see AttributeSet#getAttribute
  1284.          */
  1285.         public Object getAttribute(Object attrName) {
  1286.             return attributes.getAttribute(attrName);
  1287.         }
  1288.  
  1289.         /**
  1290.          * Gets the names of all attributes.
  1291.          *
  1292.          * @return the attribute names as an enumeration
  1293.          * @see AttributeSet#getAttributeNames
  1294.          */
  1295.         public Enumeration getAttributeNames() {
  1296.             return attributes.getAttributeNames();
  1297.         }
  1298.  
  1299.         /**
  1300.          * Checks whether a given attribute name/value is defined.
  1301.          *
  1302.          * @param name the non-null attribute name
  1303.          * @param value the attribute value
  1304.          * @return true if the name/value is defined
  1305.          * @see AttributeSet#containsAttribute
  1306.          */
  1307.         public boolean containsAttribute(Object name, Object value) {
  1308.             return attributes.containsAttribute(name, value);
  1309.         }
  1310.  
  1311.  
  1312.         /**
  1313.          * Checks whether the element contains all the attributes.
  1314.          *
  1315.          * @param attrs the attributes to check
  1316.          * @return true if the element contains all the attributes
  1317.          * @see AttributeSet#containsAttributes
  1318.          */
  1319.         public boolean containsAttributes(AttributeSet attrs) {
  1320.             return attributes.containsAttributes(attrs);
  1321.         }
  1322.  
  1323.         /**
  1324.          * Gets attributes from the parent.
  1325.          * If not overriden, the resolving parent defaults to 
  1326.          * the parent element.
  1327.          *
  1328.          * @return the attributes from the parent
  1329.          * @see AttributeSet#getResolveParent
  1330.          */
  1331.         public AttributeSet getResolveParent() {
  1332.             return attributes.getResolveParent();
  1333.         }
  1334.  
  1335.         // --- MutableAttributeSet ----------------------------------
  1336.         // should fetch a new immutable record for the field
  1337.         // "attributes".
  1338.  
  1339.         /**
  1340.          * Adds an attribute.
  1341.          *
  1342.          * @param name the non-null attribute name
  1343.          * @param value the attribute value
  1344.          * @see MutableAttributeSet#addAttribute
  1345.          */
  1346.         public void addAttribute(Object name, Object value) {
  1347.             StyleContext context = StyleContext.this;
  1348.             attributes = context.addAttribute(attributes, name, value);
  1349.             fireStateChanged();
  1350.         }
  1351.  
  1352.         /**
  1353.          * Adds a set of attributes to the element.
  1354.          *
  1355.          * @param attr the attributes to add
  1356.          * @see MutableAttributeSet#addAttribute
  1357.          */
  1358.         public void addAttributes(AttributeSet attr) {
  1359.             StyleContext context = StyleContext.this;
  1360.             attributes = context.addAttributes(attributes, attr);
  1361.             fireStateChanged();
  1362.         }
  1363.  
  1364.         /**
  1365.          * Removes an attribute from the set.
  1366.          *
  1367.          * @param name the non-null attribute name
  1368.          * @see MutableAttributeSet#removeAttribute
  1369.          */
  1370.         public void removeAttribute(Object name) {
  1371.             StyleContext context = StyleContext.this;
  1372.             attributes = context.removeAttribute(attributes, name);
  1373.             fireStateChanged();
  1374.         }
  1375.  
  1376.         /**
  1377.          * Removes a set of attributes for the element.
  1378.          *
  1379.          * @param names the attribute names
  1380.          * @see MutableAttributeSet#removeAttributes
  1381.          */
  1382.         public void removeAttributes(Enumeration names) {
  1383.             StyleContext context = StyleContext.this;
  1384.             attributes = context.removeAttributes(attributes, names);
  1385.             fireStateChanged();
  1386.         }
  1387.  
  1388.         /**
  1389.          * Removes a set of attributes for the element.
  1390.          *
  1391.          * @param attrs the attributes
  1392.          * @see MutableAttributeSet#removeAttributes
  1393.          */
  1394.         public void removeAttributes(AttributeSet attrs) {
  1395.             StyleContext context = StyleContext.this;
  1396.             if (attrs == this) {
  1397.                 attributes = context.getEmptySet();
  1398.             } else {
  1399.                 attributes = context.removeAttributes(attributes, attrs);
  1400.             }
  1401.             fireStateChanged();
  1402.         }
  1403.  
  1404.         /**
  1405.          * Sets the resolving parent.
  1406.          *
  1407.          * @param parent the parent, null if none
  1408.          * @see MutableAttributeSet#setResolveParent
  1409.          */
  1410.         public void setResolveParent(AttributeSet parent) {
  1411.             if (parent != null) {
  1412.                 addAttribute(StyleConstants.ResolveAttribute, parent);
  1413.             } else {
  1414.                 removeAttribute(StyleConstants.ResolveAttribute);
  1415.             }
  1416.         }
  1417.  
  1418.         // --- serialization ---------------------------------------------
  1419.  
  1420.         private void writeObject(ObjectOutputStream s) throws IOException {
  1421.             s.defaultWriteObject();
  1422.             writeAttributeSet(s, attributes);
  1423.         }
  1424.  
  1425.         private void readObject(ObjectInputStream s)
  1426.             throws ClassNotFoundException, IOException 
  1427.         {
  1428.             s.defaultReadObject();
  1429.             attributes = SimpleAttributeSet.EMPTY;
  1430.             readAttributeSet(s, this);
  1431.         }
  1432.  
  1433.         // --- member variables -----------------------------------------------
  1434.  
  1435.         /**
  1436.          * The change listeners for the model.
  1437.          */
  1438.         protected EventListenerList listenerList = new EventListenerList();
  1439.  
  1440.         /**
  1441.          * Only one ChangeEvent is needed per model instance since the
  1442.          * event's only (read-only) state is the source property.  The source
  1443.          * of events generated here is always "this".
  1444.          */
  1445.         protected ChangeEvent changeEvent = null;
  1446.  
  1447.         /**
  1448.          * Inner AttributeSet implementation, which may be an 
  1449.          * immutable unique set being shared.
  1450.          */
  1451.         private transient AttributeSet attributes;
  1452.  
  1453.     }
  1454.  
  1455.  
  1456. }
  1457.